// Copyright 2013 Google Inc. All Rights Reserved.
//
// The entry point of bionic's dynamic linker/loader on NaCl.
//

#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>

#include <irt_syscalls.h>
#include <private/at_sysinfo.h>

unsigned __nacl_linker_args(int envc, char** envp, int argc, char** argv);
unsigned __nacl_linker_init(unsigned **elfdata);
void __init_irt_from_auxv(unsigned *auxv);


void _start(unsigned **info) {
  int envc = (int)info[1];
  int argc = (int)info[2];
  char **argv = (char**)&info[3];
  char **envp = argv + argc + 1;
  unsigned *auxv = (unsigned *)(envp + envc + 1);
  int i, j;
  unsigned entry;
  int arg_off = 0;
  unsigned **elfdata;

  /* Initialize IRT table. sel_ldr passes __nacl_irt_query by AT_SYSINFO */
  __init_irt_from_auxv(auxv);

	

  /* As we will shift |argv| to remove the loader from it and we will
   * fill some auxv entries, we cannot reuse |info| so we will
   * allocate |elfdata| on stack of this function.
   *
   * Also note that we must share this data with both the loader and
   * the main program because the loader passes the pointer to this
   * region to the main program using TLS_SLOT_BIONIC_PREINIT.
   *
   * +--------------------------------------+ <- __nacl_linker_init and
   * | fini                                 |    entry point of main
   * +--------------------------------------+    program take this pointer.
   * | envc                                 |
   * +--------------------------------------+ <- __linker_init takes this.
   * | argc                                 |
   * +--------------------------------------+
   * .                                      |    Contains loader arguments
   * . loader_argv                          | <- and main program args.
   * . main_argv                            |    We need to remove the
   * |                                      |    linker args after processing
   * |                                      |    them.
   * |                                      |
   * +--------------------------------------+
   * | NULL                                 |
   * +--------------------------------------+
   * |                                      |
   * .                                      |
   * . envp                                 |
   * .                                      |
   * |                                      |
   * +--------------------------------------+
   * | NULL                                 | ^
   * +--------------------------------------+ |   Fields before here are
   * | AT_SYSINFO        auxv (12 elements) | +-- filled by this function.
   * | __nacl_irt_query                     |
   * | AT_BASE                              | +-- __nacl_linker_init will
   * | 0x20000 (base address of the loader) | |   fill auxv after AT_BASE.
   * | AT_PHDR                              | v
   * | Program header for main program      |
   * | AT_PHNUM                             |
   * | # of program headers in main program |
   * | AT_ENTRY                             |
   * | Entry address of main program        |
   * | AT_NULL                              |
   * | NULL                                 |
   * +--------------------------------------+
   */

  int args_used = __nacl_linker_args(envc, envp, argc, argv);

  /* For chrome, we normally don't get a command-line, so add main.nexe
   * as the default to load. */
  if (argc <= args_used) {
    argv[0] = "main.nexe";
    args_used--;
  }

  /* 3 for fini, envc, and argc. The number of argv will be decreased
   * by the number of args consumed by the loader, and plus one for both
   * argv and envp for their NULL terminators. We will have 12
   * elements in auxv. */
  elfdata = (unsigned **)alloca((3 + (argc - args_used) + 1 + envc + 1 + 12) *
                                sizeof(unsigned *));
  j = 0;
  elfdata[j++] = (unsigned *)info[0];
  elfdata[j++] = (unsigned *)envc;

  elfdata[j++] = (unsigned *)(argc - args_used);
  for (i = args_used; i < argc; i++)
    elfdata[j++] = (unsigned *)argv[i];

  elfdata[j++] = NULL;
  for (i = 0; i < envc; i++)
    elfdata[j++] = (unsigned *)envp[i];
  elfdata[j++] = NULL;
  elfdata[j++] = (unsigned *)AT_SYSINFO;
  elfdata[j++] = (unsigned *) _IRT_SYSCALL_PTR;
  /* This field will be updated in __nacl_linker_init in
   * bionic/linker/linker.cpp. */
  elfdata[j++] = (unsigned *)AT_NULL;
  elfdata[j] = NULL;

  entry = __nacl_linker_init(&elfdata[0]);
  if (!elfdata[j]) {
    static const char kErrorMsg[] = "__nacl_linker_init did not update auxv";
    static const int kStderrFd = 2;
    write(kStderrFd, kErrorMsg, sizeof(kErrorMsg) - 1);
    exit(1);
  }

  ((void (*)(unsigned **))entry)(elfdata);
}
